/** 
 * @author Riccardo Manfrin [namesurname at gmail dot com]
 * 
 * @brief  TCP control channel for optional receiver to
 * transmitter syncronization.
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>		/* close*/
#include <string.h>
#include <arpa/inet.h>  	/* inet_addr*/
#include <linux/if.h>           /* ifconf, ifreq, SIOCGIFCONF*/
#include <sys/ioctl.h>          /* ioctls */
#include <arpa/inet.h>          /* htonl */
#include "control.h"
#include "srvparser.h"
#include "wifi.h"
#include "debug.h"

#define CTRL_ENABLE ctrl_enable

static struct sockaddr_in loc_addr;
static struct sockaddr_in rmt_addr;
static char* iface = NULL;   //Flag to know if interface or IP address was used.
static int sockfd = 0;       //Socket fd
static char ctrl_enable = 0; //Control channel enable flag
static ctrl_ack ctrlAck;    //Store server replies (CNT side)

/****************** COMMON STATIC FUNCTIONALITIES ******************/
enum err_code{
  TCP_SOCKET_FAILURE,
  IOCTL_FAILURE,
  IFACE_NOT_FOUND,
  IP_NOT_FOUND,
  TCP_BIND_FAILURE,
  TCP_LISTEN_FAILURE,
  TCP_CONNECTION_REJECTED,
  TCP_CONNECTION_FAILURE,
  TCP_SELECT_FALURE,
  TCP_RX_FAILURE,
  TCP_TX_FAILURE,
  TCP_SRV_REPLY_FAILURE,
  TCP_CNT_RX_FAILURE
};

static void error(int e);

/** 
 * Function responsible of retrieving the
 * local socket IP from the interface
 */
static int ctrl_get_ip_from_iface();

/** 
 * Function responsible of retrieving the
 * local socket interface from the IP
 */
static int ctrl_get_iface_from_ip();

static void error(int e)
{
  printf("control.c : ERR_CODE %i\n",(int)e);
  return;
}

static int ctrl_get_ip_from_iface(){
  char *src_ip = NULL;
  struct in_addr addr;
  int cntr=1;
  struct ifconf ifc;
  ifc.ifc_req = malloc(sizeof(struct ifreq)*cntr);
  do{
    free(ifc.ifc_req);
    ifc.ifc_req = malloc(sizeof(struct ifreq)*cntr);
    ifc.ifc_len = sizeof(struct ifreq)*cntr;
    if(ioctl(sockfd, SIOCGIFCONF, &ifc)<0) 
    {
      error(IOCTL_FAILURE);
      return -1;
    }
    cntr++;
  } while(ifc.ifc_len==(cntr-1)*sizeof(struct ifreq));
  do {
    if(memcmp(ifc.ifc_ifcu.ifcu_req[cntr].ifr_ifrn.ifrn_name, iface,strlen(iface))==0){
      memcpy((unsigned char*) &addr.s_addr, (unsigned char*)ifc.ifc_ifcu.ifcu_req[cntr].ifr_ifru.ifru_addr.sa_data+2, 10);
      src_ip=malloc(strlen(inet_ntoa(addr))+1);
      memset(src_ip,0,strlen(inet_ntoa(addr))+1);
      memcpy(src_ip,inet_ntoa(addr),strlen(inet_ntoa(addr)));
      dbg_printf(5, "control.c: \tADDR FOUND FROM IFACE (%s)\n", src_ip);
      loc_addr.sin_addr.s_addr = inet_addr(src_ip);
    }
  } while(cntr--);
  if(src_ip==NULL) error(IP_NOT_FOUND);
  free(ifc.ifc_req);
  return 0;
}

static int ctrl_get_iface_from_ip(){
  unsigned char *addr;
  int cntr=1;
  unsigned char srcaddr[4];
  memcpy(srcaddr, (char*)&loc_addr.sin_addr.s_addr,4);
  struct ifconf ifc;
  ifc.ifc_req = malloc(sizeof(struct ifreq)*cntr);
  do{
    free(ifc.ifc_req);
    ifc.ifc_req = malloc(sizeof(struct ifreq)*cntr);
    ifc.ifc_len = sizeof(struct ifreq)*cntr;
    if(ioctl(sockfd, SIOCGIFCONF, &ifc)<0) 
    {
      error(IOCTL_FAILURE);
      return -1;
    }
    cntr++;
  } while(ifc.ifc_len==(cntr-1)*sizeof(struct ifreq));
  do {
    addr=(unsigned char*)ifc.ifc_ifcu.ifcu_req[cntr].ifr_ifru.ifru_addr.sa_data+2;
    //printf("**** %i.%i.%i.%i",addr[0],addr[1],addr[2],addr[3]);
    //printf("**** %i.%i.%i.%i\n",srcaddr[0],srcaddr[1],srcaddr[2],srcaddr[3]);
    if(memcmp(addr, &srcaddr,4)==0){
      iface=malloc(IFNAMSIZ);
      memset(iface,0,IFNAMSIZ);
      memcpy(iface, ifc.ifc_ifcu.ifcu_req[cntr].ifr_ifrn.ifrn_name, IFNAMSIZ);
      dbg_printf(5, "control.c: \tIFACE FOUND FROM ADDR (%s)\n", iface);
    }
  } while(cntr--);
  if(iface==NULL) error(IFACE_NOT_FOUND);
  free(ifc.ifc_req);
  return 0;
}

/****************** COMMON EXTERNAL FUNCTIONALITIES ******************/

void ctrl_set_iface(char *interface){
  int sz=strlen(interface);
  iface=malloc(sz+1);
  memset(iface,0,sz+1);
  memcpy(iface,interface,sz);
  return;
}

/****************** SERVER SIDE (RECEIVER) ******************/

/************** TCP SERVER SETUP ***************/
int ctrl_srv_setup(struct sockaddr_in *locaddr){
  int err=0;
  fd_set fdr;
  unsigned int client_len=0;
  int client_fd=0;
  struct sockaddr_in client_address;
  
  dbg_printf(5, "control.c: \tTCP CONTROL SERVER SETUP\n");
  
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  
  //Getting the IP/IFACE from the IFACE/IP after socket is opened
  if(iface!=NULL) ctrl_get_ip_from_iface();
  
  memcpy(&loc_addr, locaddr, sizeof(struct sockaddr_in));
  
  if(iface==NULL) ctrl_get_iface_from_ip();
  
  
  /** BINDING SOCKET TO ADDRESS **/
  err = bind(sockfd, (struct sockaddr *) &loc_addr, sizeof(struct sockaddr_in));
  
  if(err<0)
  {
    error(TCP_BIND_FAILURE);
    return -1;
  }
  
  /** STARTING SOCKET LISTEN **/
  err = listen(sockfd, MAX_PENDING_CONNECTIONS);
  
  if(err<0)
  {
    error(TCP_LISTEN_FAILURE);
    return -1;
  }
  
  dbg_printf(5, "control.c: \tTCP CONTROL CHANNEL ENABLED\n");
  
  /** 
   * accept function returns a new socket fd, that
   * must be used to receive; this is because the 
   * socket is connection oriented, therefore we 
   * must identify a connection to receive from
   * that, and this can't be done by just using the
   * server fd.
   */
  // BLOCKING FUNCTION
  client_fd = accept(sockfd,
                      (struct sockaddr *)&client_address,
                      &client_len);
  if(client_fd<0)
  {
    error(TCP_CONNECTION_REJECTED);
    return -1;
  }
  else
  {
    close(sockfd);
    dbg_printf(5, "control.c: \tTCP CONNECTION ACCEPTED (%i)\n", client_fd);
  }
  while(1){
    FD_ZERO( &fdr );
    FD_SET( client_fd, &fdr );
    FD_SET( STDIN, &fdr );
    
    if ( select( client_fd + 1, &fdr, 0, 0, NULL ) < 0 )
    {
      error(TCP_SELECT_FALURE);
    }
    if ( FD_ISSET( client_fd, &fdr ) ){
      char *buff= malloc(CMD_BUFF_SZ);
      memset(buff,0,sizeof(CMD_BUFF_SZ));
      err = recv(client_fd, buff, CMD_BUFF_SZ, 0);
      if(err<0)
      {
        error(TCP_RX_FAILURE);
      }
      if(err==0)
      {
        dbg_printf(5, "control.c: \tTCP CONNECTION CLOSED BY CLIENT\n");
        return 0;
      }
      dbg_printf(3, "control.c: \tRECEIVED PACKET (%iB)\n", err);
	/**
	* Parsing message
	*/
      ctrl_ack *ack = srvparser_cmd(buff);
	/**
	* Replying
	*/
      ctrl_srv_reply(client_fd, (char *) ack, sizeof(ctrl_ack));
	/**
	* Post synchronization with the command
	*/
      postSynchAction *pSA = srvparser_getpSA();
      if(pSA->type == SETCH) err = wifi_setCh(*((int*)pSA->postSynchData));
      free(pSA->postSynchData);
      memset(pSA,0, sizeof(postSynchAction));
    }
  }
  return 0;
}

/***************** SERVER ACK ******************/
int ctrl_srv_reply(int fd, char *data, int length){
  int err = 0;
  err = send(fd, data, length, 0);
  if(err<0) 
  {
    error(TCP_SRV_REPLY_FAILURE);
    return -1;
  }
  dbg_printf(3, "control.c: \tSERVER REPLY SENT (%iB)\n", err);
  return 0;
}

/***************** CLIENT SIDE (TRANSMITTER) *****************/

/************** TCP CLIENT SETUP ***************/
int ctrl_cnt_setup(struct sockaddr_in *locaddr, struct sockaddr_in *rmtaddr){
  int err=0;
  
  dbg_printf(5, "control.c: \tTCP CONTROL CLIENT SETUP\n");

  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if(sockfd<0) 
  {
    error(TCP_SOCKET_FAILURE);
    return -1;
  }
  
  //Getting the IP/IFACE from the IFACE/IP after socket is opened
  if(iface!=NULL) ctrl_get_ip_from_iface();
  
  memcpy(&rmt_addr, rmtaddr, sizeof(struct sockaddr_in));
  memcpy(&loc_addr, locaddr, sizeof(struct sockaddr_in));
  
  if(iface==NULL) ctrl_get_iface_from_ip();
  
  /** BINDING SOCKET TO ADDRESS **/
  err = bind(sockfd, (struct sockaddr *) &loc_addr, sizeof(struct sockaddr_in));
  
  if(err<0)
  {
    error(TCP_BIND_FAILURE);
    return -1;
  }
  
  /** INITIATING CONNECTION **/
  err = connect(sockfd, (struct sockaddr *) &rmt_addr, sizeof(struct sockaddr_in));
  
  if(err<0)
  {
    error(TCP_CONNECTION_FAILURE);
    return -1;
  }
  dbg_printf(5, "control.c: \tTCP CONTROL CHANNEL ENSTABLISHED\n");
  
  return 0;
}

/************ CLIENT REQUEST SEND **************/
int ctrl_cnt_send(char *data, int size){
  int err=0;
  err = send(sockfd, data, size, 0);
  if(err<0)
  {
    error(TCP_TX_FAILURE);
    return -1;
  }
  
  data[size-1]=0;
  dbg_printf(3, "control.c: \tTCP CLIENT CMD NOTIFICATION SENT (%s)\n", data);
  return 0;
}

/********* CLIENT REPLY WAIT & PARSE ***********/
ctrl_ack *ctrl_cnt_wack(){
  int err=0;
  /**
  * setting the ctrl_ack to zero, ensure we have
  * a NACK by default.
  */
  memset(&ctrlAck,0,sizeof(ctrl_ack));
  
  /**
   * waiting for ack reception
   */
  err = recv(sockfd, &ctrlAck, sizeof(ctrl_ack), 0);
  if (err<0)
  {
    error(TCP_CNT_RX_FAILURE);
    return NULL;
  }
  dbg_printf(3, "control.c: \tTCP SERVER REPLY RECEIVED (%iB, RESULT=%i)\n", 
    err, 
    ctrlAck.result);
  return &ctrlAck;
}

/**** CLIENT CHECK ON CONTROL CHANNEL ENABLE ***/
int ctrl_cnt_cce(){
  if(CTRL_ENABLE)
  {
    return 0;
  }
  return -1;
}

/********* CLIENT SET CONTROL CHANNEL **********/
void ctrl_cnt_set_cce(){
  CTRL_ENABLE=1;
  return;
}

